/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Master;

import Core.Dispatcher;
import Message.GetSlaveMessage;
import Message.SlaveReportMessage;
import MessageHandler.IMessageHandler;
import MessageHandler.IMessageProvider;
import MessageHandler.SlaveReportMessageHandler;
import Slave.SlaveInfo;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 *
 * @author Mr Bao
 */
public class MasterServer {

    protected static IMessageProvider messageProvider;
    protected Dispatcher dispatcher;
    protected Thread dispatcherThread;
    private ServerSocket server;
    private List<SlaveInfo> slaveInfos = new ArrayList<SlaveInfo>();
    protected boolean isRunning = true;
    private int port = 1009;

    public MasterServer() throws IOException {
        server = new ServerSocket(port);
        dispatcher = new Dispatcher();
        dispatcherThread = new Thread(dispatcher);
        dispatcherThread.start();
    }

    static void Boot() {
        messageProvider = new MessageProvider();
        messageProvider.Register(SlaveReportMessage.class, new SlaveReportMessageHandler());
        messageProvider.Register(GetSlaveMessage.class, new SlaveReportMessageHandler());
    }

    static {
        Boot();
    }

    public Dispatcher GetDispatcher() {
        return dispatcher;
    }

    public void Halt() {
        isRunning = false;
    }

    public void AddSlaveInfo(SlaveInfo info) {
        System.out.println("Inserting Slave : " + info.toString());
        synchronized (slaveInfos) {
            slaveInfos.add(info);
            slaveInfos.notify();
        }
    }

    public void UpdateSlaveInfo(int slaveId, long numberConnections) {
        synchronized (slaveInfos) {
            for (SlaveInfo slaveInfo : slaveInfos) {
                if (slaveInfo.getId() != slaveId) {
                    continue;
                }
                slaveInfo.setCurrentConnections(numberConnections);
                break;
            }
            slaveInfos.notify();
        }
    }

    public boolean TryGetValue(int slaveId) {
        boolean found = false;
        for (SlaveInfo info : slaveInfos) {
            if (info.getId() != slaveId) {
                continue;
            }
            found = true;
            break;
        }
        return found;
    }

    public boolean TryGetValue(String address, int port) {
        boolean found = false;
        for (SlaveInfo slaveInfo : slaveInfos) {
            if (!(slaveInfo.getAddress().equals(address) && slaveInfo.getPort() == port)) {
                continue;
            }
            found = true;
        }
        return found;
    }

    public SlaveInfo GetSlave() throws InterruptedException {
        List<SlaveInfo> list = new ArrayList<SlaveInfo>();
        synchronized (slaveInfos) {
            int x = 0;
            while (slaveInfos.size() == 0) {
                x++;
                if (x == 1) {
                    System.out.println("waiting...");
                }
                slaveInfos.wait();
            }
            for (SlaveInfo slaveInfo : slaveInfos) {
                list.add(slaveInfo);
            }
        }
        Collections.sort(list, new SlaveCompare());
        return (SlaveInfo) list.get(0);
    }

    public IMessageHandler GetMessageHandler(Class type) {
        return messageProvider.GetHandler(type);
    }

    public MasterClient CreateMasterClient(Socket client) throws IOException {
        return new MasterClient(this, client);
    }

    public void AcceptConnection() {
        while (isRunning) {
            try {
                Socket client = server.accept();
                System.out.println("connection from: " + client.toString());
                client.setTcpNoDelay(true);
                CreateMasterClient(client);
            } catch (Exception e) {
            }
        }
    }

    class SlaveCompare implements Comparator<SlaveInfo> {

        public int compare(SlaveInfo o1, SlaveInfo o2) {
            if (o1.getCurrentConnections() < o2.getCurrentConnections()) {
                return -1;
            } else if (o1.getCurrentConnections() > o2.getCurrentConnections()) {
                return 1;
            }
            return 0;
        }
    }
}
